﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Simple convolution
    /// </summary>
    public class SimpleConvolution : BaseImageFilter
    {
        private CSharpMask m_Mask = null;

        /// <summary>
        /// Initializes a new instance of the <see cref="SimpleConvolution"/> class.
        /// </summary>
        public SimpleConvolution()
            : this(new CSharpMask())
        {
            this.m_Mask.SetAll(1);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SimpleConvolution"/> class.
        /// </summary>
        /// <param name="_m">The _m.</param>
        public SimpleConvolution(CSharpMask _m)
        {
            m_Mask = _m;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SimpleConvolution"/> class.
        /// </summary>
        /// <param name="_simpleConvolution">The _simple convolution.</param>
        internal SimpleConvolution(SimpleConvolution _simpleConvolution)
        {
            m_Mask = _simpleConvolution.m_Mask;
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            SimpleConv(_bitmap, m_Mask);
            return _bitmap;
        }

        /// <summary>
        /// Simples the conv.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="sharpMask">The sharp mask.</param>
        /// <returns></returns>
        protected UnsafeBitmap SimpleConv(UnsafeBitmap _bitmap, CSharpMask sharpMask)
        {
            if (sharpMask.Factor == 0)
            {
                return _bitmap;
            }

            UnsafeBitmap bitmap = (UnsafeBitmap)_bitmap.Clone();
            BitmapData bData = bitmap.BitmapData; // bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

            UnsafeBitmap outputImage = _bitmap;
            BitmapData copyData = outputImage.BitmapData; //outputImage.LockBits(new Rectangle(0, 0, outputImage.Width, outputImage.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            
            int stride = bData.Stride;
            int stride2 = stride * 2;

            IntPtr ptr = bData.Scan0;
            IntPtr ptrOutPut = copyData.Scan0;

            unsafe
            {
                byte* p = (byte*)(void*)ptr;
                byte* pOutPut = (byte*)(void*)ptrOutPut;

                int nOffset = stride - bitmap.Width * bitmap.BytesPerPixelCount; //stride - bitmap.Width * 3;
                int nWidth = bitmap.Width - 2;
                int nHeight = bitmap.Height - 2;
                int nPixel;

                for (int x = 0; x < nWidth; x++)
                {
                    for (int y = 0; y < nHeight; y++)
                    {
                        /*
                        *Now we will process the three pixels R,G and B
                        *in a single Pass				 
                        * */
                        nPixel = (((pOutPut[2] * sharpMask.TopLeft) + (pOutPut[5] * sharpMask.TopMiddle) + (pOutPut[8] * sharpMask.TopRight) +
                            (pOutPut[stride + 2] * sharpMask.MiddleLeft) + (pOutPut[stride + 5] * sharpMask.Pixel) + (pOutPut[stride + 8] * sharpMask.MiddleRight)
                            + (pOutPut[stride2 + 2] * sharpMask.BottomLeft) + (pOutPut[stride2 + 5] * sharpMask.BottomMiddle) + (pOutPut[stride2 + 8] * sharpMask.BottomRight)) / sharpMask.Factor) + sharpMask.Offset;

                        if (nPixel < 0) nPixel = 0;
                        if (nPixel > 255) nPixel = 255;
                        pOutPut[5 + stride] = (byte)nPixel;
                        nPixel = ((((pOutPut[1] * sharpMask.TopLeft) + (pOutPut[4] * sharpMask.TopMiddle) +
                            (pOutPut[7] * sharpMask.TopRight) + (pOutPut[1 + stride] * sharpMask.MiddleLeft) +
                            (pOutPut[4 + stride] * sharpMask.Pixel) + (pOutPut[7 + stride] * sharpMask.MiddleRight) +
                            (pOutPut[1 + stride2] * sharpMask.BottomLeft) +
                            (pOutPut[4 + stride2] * sharpMask.BottomMiddle) +
                            (pOutPut[7 + stride2] * sharpMask.BottomRight))
                            / sharpMask.Factor) + sharpMask.Offset);

                        if (nPixel < 0) nPixel = 0;
                        if (nPixel > 255) nPixel = 255;
                        p[4 + stride] = (byte)nPixel;

                        nPixel = ((((pOutPut[0] * sharpMask.TopLeft) + (pOutPut[3] * sharpMask.TopMiddle) +
                            (pOutPut[6] * sharpMask.TopRight) + (pOutPut[0 + stride] * sharpMask.MiddleLeft) +
                            (pOutPut[3 + stride] * sharpMask.Pixel) +
                            (pOutPut[6 + stride] * sharpMask.MiddleRight) +
                            (pOutPut[0 + stride2] * sharpMask.BottomLeft) +
                            (pOutPut[3 + stride2] * sharpMask.BottomMiddle) +
                            (pOutPut[6 + stride2] * sharpMask.BottomRight))
                            / sharpMask.Factor) + sharpMask.Offset);

                        if (nPixel < 0) nPixel = 0;
                        if (nPixel > 255) nPixel = 255;
                        p[3 + stride] = (byte)nPixel;

                        p += 3;
                        pOutPut += 3;
                    }

                    p += nOffset;
                    pOutPut += nOffset;
                }
            }

            bitmap.Dispose(); //bitmap.UnlockBits(bData);

            return outputImage;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new SimpleConvolution(this);
        }
    }
}
